CGI 编程教程
1. 简介
CGI(Common Gateway Interface)是一种标准协议,用于在Web服务器和外部程序之间传递信息。通过CGI,Web服务器可以执行外部程序,并将程序的输出返回给客户端浏览器。Perl是一种非常适合CGI编程的语言,因为它具有强大的字符串处理能力和丰富的内置函数。
2. 环境设置
在开始CGI编程之前,确保你已经安装了Perl,并且Web服务器(如Apache)已经配置好支持CGI。
2.1 安装Perl
在大多数Linux发行版中,Perl已经预装。如果没有,可以使用包管理器安装:
sudo apt-get install perl
2.2 配置Web服务器
以Apache为例,确保CGI模块已启用,并在配置文件中指定CGI脚本目录:
sudo a2enmod cgi
sudo systemctl restart apache2
在Apache配置文件(如/etc/apache2/sites-available/000-default.conf)中添加:
<Directory "/var/www/cgi-bin">
Options +ExecCGI
AddHandler cgi-script .cgi .pl
</Directory>
3. 第一个CGI程序
创建一个简单的Perl CGI脚本,并将其放在CGI脚本目录中(如/var/www/cgi-bin/)。
#!/usr/bin/perl
use strict;
use warnings;
print "Content-type: text/html\n\n";
print "<html><body>";
print "<h1>Hello, CGI World!</h1>";
print "</body></html>";
保存为hello.cgi,并赋予执行权限:
chmod +x /var/www/cgi-bin/hello.cgi
在浏览器中访问http://yourserver/cgi-bin/hello.cgi,你应该会看到“Hello, CGI World!”的页面。
4. 基本语法和数据类型
4.1 标量
标量是Perl中最基本的数据类型,可以存储字符串、数字或引用。
my $name = "Alice";
my $age = 30;
4.2 数组
数组用于存储多个标量值。
my @fruits = ("apple", "banana", "cherry");
4.3 哈希表
哈希表用于存储键值对。
my %person = (
name => "Alice",
age => 30,
);
5. 变量和上下文
Perl中的变量有不同的上下文(标量上下文和列表上下文),这会影响表达式的求值方式。
my @array = (1, 2, 3);
my $count = @array; # 标量上下文,$count 为 3
my @copy = @array; # 列表上下文,@copy 为 (1, 2, 3)
6. 条件语句
6.1 if 语句
my $age = 18;
if ($age >= 18) {
print "You are an adult.\n";
}
6.2 unless 语句
my $age = 17;
unless ($age >= 18) {
print "You are not an adult.\n";
}
6.3 elsif 和 else
my $age = 20;
if ($age < 18) {
print "You are a minor.\n";
} elsif ($age < 65) {
print "You are an adult.\n";
} else {
print "You are a senior.\n";
}
7. 循环
7.1 for 循环
for (my $i = 0; $i < 5; $i++) {
print "$i\n";
}
7.2 foreach 循环
my @fruits = ("apple", "banana", "cherry");
foreach my $fruit (@fruits) {
print "$fruit\n";
}
7.3 while 循环
my $i = 0;
while ($i < 5) {
print "$i\n";
$i++;
}
7.4 until 循环
my $i = 0;
until ($i >= 5) {
print "$i\n";
$i++;
}
8. 循环控制
8.1 next
跳过当前迭代,进入下一次循环。
for (my $i = 0; $i < 5; $i++) {
next if $i == 2;
print "$i\n";
}
8.2 last
立即退出循环。
for (my $i = 0; $i < 5; $i++) {
last if $i == 2;
print "$i\n";
}
8.3 redo
重新开始当前迭代。
for (my $i = 0; $i < 5; $i++) {
redo if $i == 2;
print "$i\n";
}
9. 标量、数组、哈希表
9.1 标量
标量是Perl中最基本的数据类型,可以存储字符串、数字或引用。
my $name = "Alice";
my $age = 30;
9.2 数组
数组用于存储多个标量值。
my @fruits = ("apple", "banana", "cherry");
9.3 哈希表
哈希表用于存储键值对。
my %person = (
name => "Alice",
age => 30,
);
10. 引用和复杂数据结构
10.1 标量引用
my $scalar_ref = \$name;
print $$scalar_ref; # 输出 "Alice"
10.2 数组引用
my $array_ref = \@fruits;
print @$array_ref; # 输出 "applebananacherry"
10.3 哈希引用
my $hash_ref = \%person;
print $hash_ref->{name}; # 输出 "Alice"
11. 子程序定义和调用
11.1 定义子程序
sub greet {
my ($name) = @_;
print "Hello, $name!\n";
}
11.2 调用子程序
greet("Alice"); # 输出 "Hello, Alice!"
12. 参数传递
子程序可以通过@_数组接收参数。
sub add {
my ($a, $b) = @_;
return $a + $b;
}
my $sum = add(3, 4);
print $sum; # 输出 7
13. 返回值
子程序可以通过return语句返回值。
sub multiply {
my ($a, $b) = @_;
return $a * $b;
}
my $product = multiply(3, 4);
print $product; # 输出 12
14. 作用域和词法变量
Perl中的变量有不同的作用域,词法变量通过my关键字声明。
{
my $local_var = "local";
print $local_var; # 输出 "local"
}
print $local_var; # 错误,$local_var 未定义
15. 文件句柄
15.1 读取文件
open my $fh, "<", "file.txt" or die "Cannot open file: $!";
while (my $line = <$fh>) {
print $line;
}
close $fh;
15.2 写入文件
open my $fh, ">", "output.txt" or die "Cannot open file: $!";
print $fh "Hello, World!\n";
close $fh;
16. 目录操作
16.1 创建目录
use File::Path qw(make_path);
make_path("new_directory");
16.2 删除目录
use File::Path qw(remove_tree);
remove_tree("new_directory");
17. 文件测试操作符
17.1 检查文件是否存在
if (-e "file.txt") {
print "File exists.\n";
}
17.2 检查文件是否可读
if (-r "file.txt") {
print "File is readable.\n";
}
18. 正则表达式基础
18.1 模式匹配
my $string = "Hello, World!";
if ($string =~ /Hello/) {
print "Match found.\n";
}
18.2 替换
$string =~ s/World/Perl/;
print $string; # 输出 "Hello, Perl!"
18.3 转化
$string =~ tr/a-z/A-Z/;
print $string; # 输出 "HELLO, PERL!"
19. 高级正则表达式技巧
19.1 捕获组
my $string = "Hello, World!";
if ($string =~ /(Hello), (World)/) {
print "Greeting: $1, Subject: $2\n";
}
19.2 非捕获组
my $string = "Hello, World!";
if ($string =~ /(?:Hello), (World)/) {
print "Subject: $1\n";
}
20. 使用模块
20.1 使用内置模块
use File::Basename;
my $filename = "path/to/file.txt";
my $basename = basename($filename);
print $basename; # 输出 "file.txt"
20.2 创建自定义模块
创建一个名为MyModule.pm的文件:
package MyModule;
sub greet {
my ($name) = @_;
return "Hello, $name!";
}
1;
在主程序中使用:
use MyModule;
print MyModule::greet("Alice"); # 输出 "Hello, Alice!"
21. 命名空间
Perl中的命名空间用于组织模块和避免命名冲突。
package MyNamespace::MyModule;
sub greet {
my ($name) = @_;
return "Hello, $name!";
}
1;
在主程序中使用:
use MyNamespace::MyModule;
print MyNamespace::MyModule::greet("Alice"); # 输出 "Hello, Alice!"
22. CPAN 使用
CPAN(Comprehensive Perl Archive Network)是Perl模块的集中存储库。
22.1 安装模块
cpan install Some::Module
22.2 使用模块
use Some::Module;
23. 类和对象
23.1 定义类
package Person;
sub new {
my ($class, %args) = @_;
my $self = {
name => $args{name},
age => $args{age},
};
bless $self, $class;
return $self;
}
sub greet {
my ($self) = @_;
print "Hello, I am $self->{name} and I am $self->{age} years old.\n";
}
1;
23.2 创建对象
use Person;
my $person = Person->new(name => "Alice", age => 30);
$person->greet(); # 输出 "Hello, I am Alice and I am 30 years old."
24. 继承
24.1 定义子类
package Employee;
use base 'Person';
sub new {
my ($class, %args) = @_;
my $self = $class->SUPER::new(%args);
$self->{job} = $args{job};
return $self;
}
sub job {
my ($self) = @_;
print "My job is $self->{job}.\n";
}
1;
24.2 使用子类
use Employee;
my $employee = Employee->new(name => "Bob", age => 25, job => "Developer");
$employee->greet(); # 输出 "Hello, I am Bob and I am 25 years old."
$employee->job(); # 输出 "My job is Developer."
25. 多态
多态允许不同的类以相同的方式调用方法。
package Animal;
sub speak {
die "Abstract method";
}
1;
package Dog;
use base 'Animal';
sub speak {
print "Woof!\n";
}
1;
package Cat;
use base 'Animal';
sub speak {
print "Meow!\n";
}
1;
使用多态:
use Dog;
use Cat;
my @animals = (Dog->new(), Cat->new());
foreach my $animal (@animals) {
$animal->speak();
}
26. 封装
封装是将数据和操作数据的方法组合在一起,并通过公共接口访问。
package BankAccount;
sub new {
my ($class, %args) = @_;
my $self = {
balance => $args{balance} || 0,
};
bless $self, $class;
return $self;
}
sub deposit {
my ($self, $amount) = @_;
$self->{balance} += $amount;
}
sub withdraw {
my ($self, $amount) = @_;
$self->{balance} -= $amount;
}
sub get_balance {
my ($self) = @_;
return $self->{balance};
}
1;
使用封装:
use BankAccount;
my $account = BankAccount->new(balance => 100);
$account->deposit(50);
$account->withdraw(30);
print $account->get_balance(); # 输出 120
27. 标量引用、数组引用、哈希引用
27.1 标量引用
my $scalar_ref = \$name;
print $$scalar_ref; # 输出 "Alice"
27.2 数组引用
my $array_ref = \@fruits;
print @$array_ref; # 输出 "applebananacherry"
27.3 哈希引用
my $hash_ref = \%person;
print $hash_ref->{name}; # 输出 "Alice"
28. 子程序引用
sub add {
my ($a, $b) = @_;
return $a + $b;
}
my $func_ref = \&add;
print $func_ref->(3, 4); # 输出 7
29. 闭包
闭包是一个函数,它可以访问其词法作用域中的变量。
sub create_counter {
my $count =